﻿/* 
Copyright (c) 2014 Eric Follea

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Facet4Windows.Plugin
{
    public class PluginController
    {
        public IDictionary<String, IEnvironmentController> controllers;

        private System.IO.DirectoryInfo pluginDirectory;

        public PluginController(String relativepath) : this(new DirectoryInfo(relativepath)) { }

        public PluginController(System.IO.DirectoryInfo where)
        {
            this.pluginDirectory = where;
            this.controllers = new Dictionary<String, IEnvironmentController>();
        }

        private System.IO.FileInfo[] dllFiles()
        {
            System.IO.FileInfo[] dllFileNames = null;
            if (pluginDirectory.Exists)
            {
                dllFileNames = pluginDirectory.GetFiles("*.dll");
            }
            return dllFileNames;
        }



        public void load()
        {
            System.IO.FileInfo[] dlls = dllFiles();

            ICollection<Assembly> assemblies = new List<Assembly>(dlls.Length);
            foreach (System.IO.FileInfo dllFile in dlls)
            {
                AssemblyName an = AssemblyName.GetAssemblyName(dllFile.FullName);
                Assembly assembly = Assembly.Load(an);
                assemblies.Add(assembly);
            }



            //-----

            List<IEnvironmentController> controllers = loadTypes(assemblies, typeof(IEnvironmentController)).
                Select(controllerType => (IEnvironmentController)Activator.CreateInstance(controllerType)).ToList();

            controllers.ForEach(controller =>
                 ShemesOf(controller).ForEach(sheme =>
                     {
                         if (this.controllers.ContainsKey(sheme))
                         {
                             addWarning(sheme, controller);
                         }
                         else
                         {
                             this.controllers.Add(sheme, controller);
                         }
                     }
                     ));
        }

        private void addWarning(string sheme, IEnvironmentController i)
        {
            Type type = this.controllers[sheme].GetType();
            throw new Exception(sheme + " already binded with controller of type " + type + " in " + type.Assembly);
        }

        private List<string> ShemesOf(IEnvironmentController i)
        {
            return i.GetType().GetCustomAttributes<SchemeInfoAttribute>().Select(t => t.Value).ToList();
        }

        private IEnumerable<Type> loadTypes(ICollection<Assembly> assemblies, Type pluginType)
        {
            return (from assembly in assemblies
                    where assembly != null
                    select (from type in assembly.GetTypes()
                            where (!(type.IsInterface || type.IsAbstract) && type.GetInterface(pluginType.FullName) != null)
                            select type)).SelectMany(k => k)
                     ;
        }

    }
}
